#include "exceptions.h"
#include "of-devtree.h"
+/* Secondary processors use this for handshaking with main processor. */
+volatile unsigned int __spin_ack;
+
static ulong of_vec;
static ulong of_msr;
static int of_out;
static int __init boot_of_cpus(void)
{
int cpus;
- int cpu;
+ int cpu, bootcpu, logical;
int result;
u32 cpu_clock[2];
cpu_khz /= 1000;
of_printf("OF: clock-frequency = %ld KHz\n", cpu_khz);
- /* FIXME: should not depend on the boot CPU bring the first child */
+ /* Look up which CPU we are running on right now. */
+ result = of_getprop(bof_chosen, "cpu", &bootcpu, sizeof (bootcpu));
+ if (result == OF_FAILURE)
+ of_panic("Failed to look up boot cpu\n");
+
cpu = of_getpeer(cpu);
- while (cpu > 0) {
- of_start_cpu(cpu, (ulong)spin_start, 0);
+
+ /* We want a continuous logical cpu number space. */
+ cpu_set(0, cpu_present_map);
+ cpu_set(0, cpu_online_map);
+
+ /* Spin up all CPUS, even if there are more than NR_CPUS, because
+ * Open Firmware has them spinning on cache lines which will
+ * eventually be scrubbed, which could lead to random CPU activation.
+ */
+ for (logical = 1; cpu > 0; logical++) {
+ unsigned int cpuid, ping, pong;
+ unsigned long now, then, timeout;
+
+ if (cpu == bootcpu) {
+ of_printf("skipping boot cpu!\n");
+ continue;
+ }
+
+ result = of_getprop(cpu, "reg", &cpuid, sizeof(cpuid));
+ if (result == OF_FAILURE)
+ of_panic("cpuid lookup failed\n");
+
+ of_printf("spinning up secondary processor #%d: ", logical);
+
+ __spin_ack = ~0x0;
+ ping = __spin_ack;
+ pong = __spin_ack;
+ of_printf("ping = 0x%x: ", ping);
+
+ mb();
+ result = of_start_cpu(cpu, (ulong)spin_start, logical);
+ if (result == OF_FAILURE)
+ of_panic("start cpu failed\n");
+
+ /* We will give the secondary processor five seconds to reply. */
+ then = mftb();
+ timeout = then + (5 * timebase_freq);
+
+ do {
+ now = mftb();
+ if (now >= timeout) {
+ of_printf("BROKEN: ");
+ break;
+ }
+
+ mb();
+ pong = __spin_ack;
+ } while (pong == ping);
+ of_printf("pong = 0x%x\n", pong);
+
+ if (pong != ping)
+ cpu_set(logical, cpu_present_map);
+
cpu = of_getpeer(cpu);
}
return 1;
boot_of_rtas();
/* end of OF */
+ of_printf("Quiescing Open Firmware ...\n");
of_call("quiesce", 0, 0, NULL);
return &mbi;
mtmsrd r3
blr
+/* The primary processor issues a firmware call to spin us up at this
+ * address, passing our CPU number in r3. We only need a function
+ * entry point instead of a descriptor since this is never called from
+ * C code.
+ */
.globl spin_start
spin_start:
+ /* Write our processor number as an acknowledgment that we're alive. */
+ LOADADDR(r14, __spin_ack)
+ stw r3, 0(r14)
+ sync
+ /* If NR_CPUS is too small, we should just spin forever. */
+ LOADADDR(r15, NR_CPUS)
+ cmpd r3, r15
+ blt 2f
+ b .
+ /* Find our index in the array of processor_area struct pointers. */
+2: LOADADDR(r14, global_cpu_table)
+ muli r15, r3, 8
+ add r14, r14, r15
+ /* Spin until the pointer for our processor goes valid. */
+1: ld r15, 0(r14)
+ cmpldi r15, 0
+ beq 1b
+ /* Dereference the pointer and load our stack pointer. */
+ isync
+ ld r1, PAREA_stack(r15)
+ li r14, STACK_FRAME_OVERHEAD
+ sub r1, r1, r14
+ /* Load up the TOC and entry point for the C function to be called. */
+ LOADADDR(r14, secondary_cpu_init)
+ ld r2, 8(r14)
+ ld r11, 0(r14)
+ mtctr r11
+ /* Warning: why do we need this synchronizing instruction on 970FX? */
+ isync
+ /* Jump into C code now. */
+ bctrl
+ nop
b .
#undef SERIALIZE
+extern volatile struct processor_area * volatile global_cpu_table[];
+
unsigned int cpu_rma_order(void)
{
/* XXX what about non-HV mode? */
return rma_log_size - PAGE_SHIFT;
}
-void cpu_initialize(void)
+void cpu_initialize(int cpuid)
{
- ulong stack;
+ ulong r1, r2;
+ __asm__ __volatile__ ("mr %0, 1" : "=r" (r1));
+ __asm__ __volatile__ ("mr %0, 2" : "=r" (r2));
- parea = xmalloc(struct processor_area);
+ /* This is SMP safe because the compiler must use r13 for it. */
+ parea = global_cpu_table[cpuid];
ASSERT(parea != NULL);
- stack = (ulong)alloc_xenheap_pages(STACK_ORDER);
-
- ASSERT(stack != 0);
- parea->hyp_stack_base = (void *)(stack + STACK_SIZE);
- printk("stack is here: %p\n", parea->hyp_stack_base);
-
mthsprg0((ulong)parea); /* now ready for exceptions */
/* Set decrementers for 1 second to keep them out of the way during
s |= 1UL << (63-3); /* ser-gp */
hid0.word |= s;
#endif
- printk("hid0: 0x%016lx\n", hid0.word);
+
+ printk("CPU #%d: Hello World! SP = %lx TOC = %lx HID0 = %lx\n",
+ smp_processor_id(), r1, r2, hid0.word);
+
mthid0(hid0.word);
union hid1 hid1;
int opt_earlygdb = 0;
boolean_param("earlygdb", opt_earlygdb);
+/* opt_nosmp: If true, secondary processors are ignored. */
+static int opt_nosmp = 0;
+boolean_param("nosmp", opt_nosmp);
+
+/* maxcpus: maximum number of CPUs to activate. */
+static unsigned int max_cpus = NR_CPUS;
+integer_param("maxcpus", max_cpus);
+
u32 tlbflush_clock = 1U;
DEFINE_PER_CPU(u32, tlbflush_time);
cpumask_t cpu_sibling_map[NR_CPUS] __read_mostly;
cpumask_t cpu_online_map; /* missing ifdef in schedule.c */
+cpumask_t cpu_present_map;
/* XXX get this from ISA node in device tree */
ulong isa_io_base;
/* move us to a header file */
extern void initialize_keytable(void);
+volatile struct processor_area * volatile global_cpu_table[NR_CPUS];
+
int is_kernel_text(unsigned long addr)
{
if (addr >= (unsigned long) &_start &&
return ALIGN_UP(end, PAGE_SIZE);
}
+static void init_parea(int cpuid)
+{
+ /* Be careful not to shadow the global variable. */
+ volatile struct processor_area *pa;
+ void *stack;
+
+ pa = xmalloc(struct processor_area);
+ if (pa == NULL)
+ panic("%s: failed to allocate parea for cpu #%d\n", __func__, cpuid);
+
+ stack = alloc_xenheap_pages(STACK_ORDER);
+ if (stack == NULL)
+ panic("%s: failed to allocate stack (order %d) for cpu #%d\n",
+ __func__, STACK_ORDER, cpuid);
+
+ pa->whoami = cpuid;
+ pa->hyp_stack_base = (void *)((ulong)stack + STACK_SIZE);
+
+ /* This store has the effect of invoking secondary_cpu_init. */
+ global_cpu_table[cpuid] = pa;
+ mb();
+}
+
+static int kick_secondary_cpus(int maxcpus)
+{
+ int cpuid;
+
+ for_each_present_cpu(cpuid) {
+ if (cpuid == 0)
+ continue;
+ if (cpuid >= maxcpus)
+ break;
+ init_parea(cpuid);
+ cpu_set(cpuid, cpu_online_map);
+ }
+
+ return 0;
+}
+
+/* This is the first C code that secondary processors invoke. */
+int secondary_cpu_init(int cpuid, unsigned long r4);
+int secondary_cpu_init(int cpuid, unsigned long r4)
+{
+ cpu_initialize(cpuid);
+ while(1);
+}
+
static void __init __start_xen(multiboot_info_t *mbi)
{
char *cmdline;
percpu_init_areas();
- cpu_initialize();
+ init_parea(0);
+ cpu_initialize(0);
#ifdef CONFIG_GDB
initialise_gdb();
debugger_trap_immediate();
#endif
+ /* Deal with secondary processors. */
+ if (opt_nosmp) {
+ printk("nosmp: leaving secondary processors spinning forever\n");
+ } else {
+ printk("spinning up at most %d total processors ...\n", max_cpus);
+ kick_secondary_cpus(max_cpus);
+ }
+
start_of_day();
/* Create initial domain 0. */
{
}
+
+
/*
* Local variables:
* mode: C
#define CONFIG_GDB 1
#define CONFIG_SMP 1
#define CONFIG_PCI 1
-#define NR_CPUS 1
+#define NR_CPUS 16
#ifndef ELFSIZE
#define ELFSIZE 64
struct vcpu;
-register struct processor_area *parea asm("r13");
+register volatile struct processor_area *parea asm("r13");
static inline struct vcpu *get_current(void)
{
struct processor_area
{
+ unsigned int whoami;
struct vcpu *cur_vcpu;
void *hyp_stack_base;
ulong saved_regs[2];
extern void show_registers(struct cpu_user_regs *);
extern void show_execution_state(struct cpu_user_regs *);
extern unsigned int cpu_rma_order(void);
-extern void cpu_initialize(void);
+extern void cpu_initialize(int cpuid);
extern void cpu_init_vcpu(struct vcpu *);
extern void save_cpu_sprs(struct vcpu *);
extern void load_cpu_sprs(struct vcpu *);
/* revisit when we support SMP */
#define get_hard_smp_processor_id(i) i
-#define hard_smp_processor_id() 0
-#define raw_smp_processor_id() 0
+#define raw_smp_processor_id() (parea->whoami)
+#define hard_smp_processor_id() raw_smp_processor_id()
extern cpumask_t cpu_sibling_map[];
extern cpumask_t cpu_core_map[];